perm filename OC.FIX[MF,ALS] blob sn#764503 filedate 1984-08-03 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00005 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	@* OC Matrix Format.
C00010 00003	IFDOVERMODES
C00014 00004	Since the details of the header information cannot be known until much of
C00019 00005	internal procedure charclear # initializes parameters for a new character
C00033 ENDMK
C⊗;
@* OC Matrix Format.
A \.{OC} file is an expanded raster description of a single font at a
particular resolution and contains essentially the same information as
that contained in a \.{GF} file.  \.{OC} files are used by the Dover.
All words in of \.{OC} files are in 32-bit format, with the four lower
bits zero on 36-bit machines.

By convention, \.{OC} files
are for 384 pixels per inch. \.{GFtoOC} will report the magnification
over the design point size that will occur if the \.{OC} file is 
used on a 384 pixel per inch output device. Fonts specifically designed
for the Dover will report a magnification of 1000.

Data segments of type OrbitChars have an internal structure that is a
minature version of the structure of the complete dictionary file. At the
beginning of these segements, there is a table of header information that
specifies the dimensions and widths of each character in the font. Next
there is a table of file pointers that give, for each character code, the
location of the corresponding raster block. And finally, there are the
raster blocks themselves.


The raster information is contained in a sequence of binary words that
record white pixels as zeros and black pixels as ones. Furthermore, this
raster information is written in a rotated form, as compared with the
convention for \.{PXL} files, reporting the pixels as read from the bottom
left corner of the glyph, reading up the leftmost column, with this being
followed imediately by the next column, again from bottom to top, without
any unused bit locations between columns. The final half word is, however
padded out with zeros.

We will need the following arrays to accumulate information regarding all
of the glyphs as they ae beng processed:

@d char_seg_file_pos='3000 {earliest |file_pos| of individual char segments}
@d no_char_flag=-(2.0↑120) {real that won''t otherwise appear as |char_width_x|}

@ @<Glob...@>=
@!glyph_ptr: array [0..max_glyph_no] of integer; {called charsegfilepos in mf.old}
@!glyph_cols: array [0..max_glyph_no] of integer;
@!glyph_rows: array [0..max_glyph_no] of integer;
@!cols_offset: array [0..max_glyph_no] of integer;
@!rows_offset: array [0..max_glyph_no] of integer;




@!bc,ec:integer;
@!oc_dir_ptr:integer;
@!oc_mag: integer;

IFDOVERMODES
	'20000000	make Dover .OC font
	'40000000	make PrePress-style widths (.WD) file
	'100000000	use charwx and charwy to get vector style widths
ENDDOVERMODES;

IFDOVERMODES
  define ocmode=⊂(control land '20000000)⊃, wdmode=⊂(control land '40000000)⊃;
  define vectorwidths=⊂(control land '100000000)⊃;
ENDDOVERMODES

IFDOVERMODES
internaldef numberofmodes=8;
internaldef doveroc=7,presswd=8;
ENDDOVERMODES

IFDOVERMODES
define nonexistentcharflag=⊂-(2.0↑120)⊃ # a real number that won't occur
	as the vector width X component of any real character;
saf real array CharWidthX[0:'177];
saf real array CharWidthY[0:'177] # x and y components of
	the vector widths of characters;
integer bbxlmin, bbxrmax, bbylmin, bbyhmax # extremes of bounding box;
real charwxmax, charwxmin, charwymax, charwymin # extremes of width vector
	components;
define IX(typ, lngth)=⊂((typ lsh 12)+lngth)⊃;
saf integer array charsegptr[0:'177] # filepos's of individual char segments;
define charsegfilepos=⊂('3000)⊃ # earliest filepos in .oc file that a
	character segment can start (in 16-bit words), rounded up to the
	nearest multiple of 2*pagesize(For WAITS' sake!);
ENDDOVERMODES

IFDOVERMODES
[presswd] begin 
arrclr(CharWidthX,nonexistentcharflag) # mark all characters as missing;
bbxlmin←infty; bbxrmax←-infty;
bbylmin←infty; bbyhmax←-infty;
charwxmin←infty; charwxmax←-infty;
charwymin←infty; charwymax←-infty;
end;
[doveroc] begin 
for i←1 thru charsegfilepos div 2 do wordout(ochan[doveroc],0);
bytecount[doveroc]←charsegfilepos*2 # start of first character segment;
arrclr(charsegptr,-1) # mark all characters as missing;
end;
ENDDOVERMODES

IFDOVERMODES require "MFDOVR.SAI" source_file; ENDDOVERMODES

IFDOVERMODES ofilext[doveroc]←".oc"; ofilext[presswd]←".wd"; ENDDOVERMODES

IFDOVERMODES bndboxvalid←false; ENDDOVERMODES

	IFDOVERMODES 
	if ocmode then makeoc;
	if wdmode then makewd;
	ENDDOVERMODES 

IFDOVERMODES
if ochan[doveroc]≥0 then
	begin occloseout;
	binaryrelease(ochan[doveroc]);
	print(nextline,"Images written on ",flname[doveroc]);
	end;
if ochan[presswd]≥0 then
	begin wdcloseout;
	binaryrelease(ochan[presswd]);
	print(nextline,"PrePress-style widths written on ",flname[presswd]);
	end;
ENDDOVERMODES
Since the details of the header information cannot be known until much of
the work has been done toward creating the raster information itself, the
\.{OC} format arrangwes for the raster information to start at byte '3000
and we arrange for this, initially, by writing zeros in the first '3000
bytes.  We then proceed to write the raster information, one glyph at a
time, and save the necessary header information in global arrays. After
the last glyph has been processed, we then write out the header.

The header consists of a block of 24 half-words that are characteristic of the
font in general followed by a character width table and finally by the
character-segment pointers. 

The header consists of a block of 24 half-words that are characteristic of
the font in general followed by a character width table and finally by the
character-segment pointers.

There need not be a complete set of 128 glyphs in the font but it will be
assumed that the set is reasonably complete within a group from a
character number of |bc| through |ec|.  There will be a set of 8
half-words of width information for each character within this group (with
a standard notation for all missing glyphs).

There will be a similar set of |ec| $-$ |bc| $+$ 1 pointers, each
occupying two half-words, for the raster information for each character
(with pointers of $-1$ for missing characters).

The initial 24 half-words of the header will contain:
\smallskip\hang\noindent
0. A header for the family-name IX.
\smallskip\hang\noindent
1. The font name code.
\smallskip\hang\noindent
2..11.. A 20-character font identifier string.
\smallskip\hang\noindent
12. Header for orbit-chars IX.
\smallskip\hang\noindent
13. Name code again in left byte and logical size encoded as face byte.
\smallskip\hang\noindent
14. |bc| in left byte and |ec| in right byte.
\smallskip\hang\noindent
15. Physical size in micas (real).
\smallskip\hang\noindent
16. Rotation in minutes of arc.
\smallskip\hang\noindent
17..18. Starting file position of font segment.
\smallskip\hang\noindent
19..20. Font segment length.
\smallskip\hang\noindent
21. X resolution in units of pixels/(10 inchs) (real)
\smallskip\hang\noindent
22. Y resolution in units of pixels/(10 inches) (real).
\smallskip\hang\noindent
23. EndIX.
\smallskip

The 8 words of width information for each |ec|$-$|bc|$+1$ entry will contain:
\smallskip\hang\noindent
0,1. X-width$*$xresolution$*(2↑16)$.
\smallskip\hang\noindent
2,3. Y-width$*$yresolution$*(2↑16)$.
\smallskip\hang\noindent
4. Bounding box x-offset.
\smallskip\hang\noindent
5. Bounding box y-offset.
\smallskip\hang\noindent
6. Bounding box x-width in scan lines.
\smallskip\hang\noindent
7. Bounding box y-height in bits.
\smallskip

Finally the raster information for each glyph will occupy a varying amount
of space up to a limit of 
(((|right_pixel|-|left_pixel|)*(|top_pixel|-|bot_pixel|)+15)div 16 half-words,
although most glyphs will not occupy this much space.

internal procedure charclear # initializes parameters for a new character;
begin charwd←chardp←charht←charic←charwx←charwy←0.0; isvarchar←false;
chardw←0; charcode←-1;
brkptr[0]←brkptr[1]←0; brktab[0,0]←brktab[1,0]←1 lsh (bitsperwd-1);
IFDOVERMODES bndboxvalid←false; ENDDOVERMODES
end;

A COMMENT THAT MAY HELP****
comment Routines for chr mode.
In this mode we output the characters in asterisk-dot form. Exactly two
columns have more than one dot, these columns specifying the pixels to the
left and right of the character (columns -1 and chardw).
Exactly one row has more than two dots, this row being the baseline (row 0);

define charwx=⊂realparam[24]⊃ # x component of vector width; 
define charwy=⊂realparam[25]⊃ # y component of vector width; 
define charwd=⊂realparam[7]⊃ # width of character to be output;
define charht=⊂realparam[8]⊃ # height of character to be output;
define chardp=⊂realparam[9]⊃ # depth of character to be output;

comment Routines for Dover-style .oc files;

comment In this mode, we output characters as Orbitized PARC-style
.AC files with a rotation of 0 minutes.  This demands both changing to
32 bitsperword from 36, and also rotating the raster 90 degrees, because
the conventions of .AC format dictate that a non-rotated character is
scanned from bottom-to-top, left-to-right (as Dover's scan);

@ @<Glob...@>=
@!glyph_ptr: array [0..max_glyph_no] of integer;
@!glyph_cols: array [0..max_glyph_no] of integer;
@!glyph_rows: array [0..max_glyph_no] of integer;

@!cols_offset: array [0..max_glyph_no] of integer;
@!rows_offset: array [0..max_glyph_no] of integer;
@!bc,ec:integer;
@!oc_dir_ptr:integer;
@!oc_mag: integer;

saf integer array 
BBoxArray,
BBoyArray,
BBdxArray,
BBdyArray[0:'177];

bbxl←xlb; bbxr←xrb; bbyl←yl; bbyh←yh;
bbdx←xrb-xlb+1; bbdy←yh-yl+1; bbox←xlb; bboy←yl;
emptychar←false; bndboxvalid←true;

integer bbox,bboy,bbdx,bbdy,bbxl,bbxr,bbyl,bbyh # dimensions of the
	character bounding box, set by bndbox;
boolean emptychar # true iff char has empty raster, set by bndbox;
define bitloc(x)=⊂((x+(1000*bitsperwd+hw-1))mod bitsperwd)⊃ # number of
	bits to the left of bit x, copied from mfrast;
boolean bndboxvalid # true iff bndbox has been called for this character;

procedure bndbox;
comment this procedure computes the bounding box of the character in 
	pixel coordinates, since both .oc and .wd format demand it.  Leaves
	coordinates of bounding box in bb** global integers---returns
	true iff the raster is non-blank;
begin integer i,xw,y,z,xl,xr,lz,lzr,xlb,xrb,yl,yh;
label nonblank3,nonblank4;
xl←xleft; xr←xright; z←0;
loop	begin comment try to eliminate blank column at left;
	xw←xl*rspan;
	for y←xw+ylow thru xw+yhigh do
		var!gets!rast!lor!var(z,y) # z←z lor rast[y];
	if z then done;
	xl←xl+1;
	if xl>xr then
		begin comment blank raster;
		bbdx←bbdy←bbox←bboy←0;
		bbxl←0; bbxr←-1; bbyl←0; bbyh←-1 # as good as any other;
		emptychar←true; bndboxvalid←true;
		return;
		end;
	end;
lz←0; while z>0 do
	begin lz←lz+1; z←z lsh 1;
	end;
xlb←(1-hw-bitsperwd*rcol(0))+lz+bitsperwd*xl;
z←0;
loop	begin comment try to eliminate blank column at right.  The
		loop is guaranteed to halt, since raster is non-empty;
	xw←xr*rspan;
	for y←xw+ylow thru xw+yhigh do
		var!gets!rast!lor!var(z,y) # z←z lor rast[y];
	if z then done;
	xr←xr-1;
	end;
comment Assert z≠0;
lzr←rightmostbitindex(z);
xrb←(1-hw-bitsperwd*rcol(0))+lzr+bitsperwd*xr;
yl←ylow; yh←yhigh;
loop	begin comment try to eliminate blank row at bottom;
	for xw←xl*rspan+yl step rspan until xr*rspan+yl do
IFXMEM	begin var!gets!rast(xtemp,xw); if xtemp then go to nonblank3; end;
ELSEC		if rast[xw] then go to nonblank3;
ENDC
	yl←yl+1;
	end;
nonblank3:
loop	begin comment try to eliminate blank row at top;
	for xw←xl*rspan+yh step rspan until xr*rspan+yh do
IFXMEM	begin var!gets!rast(xtemp,xw); if xtemp then go to nonblank4; end;
ELSEC		if rast[xw] then go to nonblank4;
ENDC
	yh←yh-1;
	end;
nonblank4:
bbxl←xlb; bbxr←xrb; bbyl←yl; bbyh←yh;
bbdx←xrb-xlb+1; bbdy←yh-yl+1; bbox←xlb; bboy←yl;
emptychar←false; bndboxvalid←true;
end;

procedure makeoc # outputs the current character to .oc file;
begin integer i,x,y,ch;
integer padbits, charbits, charwords;
integer coladdr, wdaddr, shft, bitptr, pfield, accum, ocfilepos;
ch←openofil(doveroc);
if not bndboxvalid then bndbox;
if charsegptr[charcode]≠-1 then error("Duplicate charcode: '"&cvos(charcode));
BBdxArray[charcode]←bbdx; BBdyArray[charcode]←bbdy;
BBoxArray[charcode]←bbox; BBoyArray[charcode]←bboy;
if not vectorwidths then
	begin
	charwx←charwd;
	charwy←0.0;
	end;
CharWidthX[charcode]←charwx;
CharWidthY[charcode]←charwy;
charbits←bbdx*bbdy;
charwords←2*((charbits+31) div 32) # orbitchars block must be
	and even number of sixteen-bit words;
padbits←16*charwords-charbits;
charsegptr[charcode]←bytecount[doveroc] div 2 # bytes to 16-bit words;

comment Send the character segment out to the file:;
Wout(doveroc,-bbdy);
Wout(doveroc,bbdx-1);
accum←0; bitptr←point(1,accum,-1);
pfield←point(6,bitptr,5) # points at the "P" field of bitptr;
for x←bbxl thru bbxr do
	begin "move one column"
	coladdr←rcol(x)*rspan;
	shft←(bitloc(x)-35);
	for wdaddr←coladdr+bbyl thru coladdr+bbyh do
		begin "move one bit"
IFXMEM		var!gets!rast!lsh!expr(xtemp,wdaddr,shft); idpb(xtemp,bitptr);
ELSEC		idpb(rast[wdaddr] lsh shft,bitptr);	
ENDC
		if ldb(pfield)=4 then 
			begin
			dpb(36,pfield) # reset bitptr to left end of accum;
			DoutAligned(doveroc,accum);
			end;
		end "move one bit";
	end "move one column";
for i←1 thru padbits do idpb(0,bitptr);
if ldb(pfield)=4 then 
	begin
	dpb(36,pfield) # reset bitptr to left end of accum;
	DoutAligned(doveroc,accum);
	end;
if ldb(pfield)≠36 then confusion;
end;


procedure occloseout;
begin
integer i,c,bc,ec,nc; integer fontsegstart, fontsegend, relptrbase;
integer ch # channel for output;
ch←ochan[doveroc];
for bc←0 step 1 until '177 do if charsegptr[bc]≠-1 then done;
for ec←'177 step -1 until 0 do if charsegptr[ec]≠-1 then done;
if bc>ec then
	begin
	bc←1; ec←0;
	error("No characters in this font");
	end;
nc←ec-bc+1;
if fontfacebyte<0 or fontfacebyte>255 then
	error("Fontfacebyte out of bounds");
while rotation>360 do rotation←rotation-360;
while rotation<0 do rotation←rotation+360;
fontsegstart←charsegfilepos-(8+2)*nc;
fontsegend←bytecount[doveroc] div 2;
useto(ch,1) # reset file position to beginning;
bytecount[doveroc]←0;
Wout(doveroc,IX(1,12)) # header for family-name IX;
Wout(doveroc,0) # name code;
BCPLout(doveroc,fontidentifier,20);
Wout(doveroc,IX(5,11)) # header for orbit-chars IX;
Bout(doveroc,0) # name code again;
Bout(doveroc,fontfacebyte) # logical size encoded as face byte;
Bout(doveroc,bc); Bout(doveroc,ec);
define ppi=⊂72.27⊃ # points per inch according to TEX/Metafont;
Wout(doveroc,(designsize*magnification*2540/ppi)+0.5) # physical siz in micas;
Wout(doveroc,(60*rotation)+0.5) # rotation in minutes of arc;
Dout(doveroc,fontsegstart) # starting file pos of font segment;
Dout(doveroc,fontsegend-fontsegstart) # and font segment length;
Wout(doveroc,(xresolution*ppi*10/magnification)+0.5) # X resolution in
		units of pixels/(10 inches);
Wout(doveroc,(yresolution*ppi*10/magnification)+0.5) # Y resolution in
		units of pixels/(10 inches);
Wout(doveroc,IX(0,1)) # endIX;
comment next, write the char width table and char segment ptrs--first
	get to the correct place in the file;
DEBUGONLY if bytecount[doveroc]≠2*('30) then confusion;
for i←1 thru (fontsegstart-'30) div 2 do DoutAligned(doveroc,0);
for c←bc thru ec do
	if charsegptr[c]≠-1 then
		begin
		comment Convert the spacing Xwidth of the character
		  from points into (fixed.fraction) pixels;
		integer newwidth;
		newwidth←(CharWidthX[c]*xresolution*(2↑16))+0.5;
		Dout(doveroc,newwidth);
		newwidth←(CharWidthY[c]*yresolution*(2↑16))+0.5;
		Dout(doveroc,newwidth);
		Wout(doveroc,BBoxArray[c]);
		Wout(doveroc,BBoyArray[c]);
		Wout(doveroc,BBdxArray[c]);
		Wout(doveroc,BBdyArray[c]);
		end
	  else	begin
		integer i;
		for i←1 thru 7 do Wout(doveroc,0);
		Wout(doveroc,-1) # marks a non-existent character;
		end;
relptrbase←charsegfilepos-2*nc;
DEBUGONLY if bytecount[doveroc]≠relptrbase*2 then confusion;
for c←bc thru ec do
	if charsegptr[c]≠-1 then Dout(doveroc,charsegptr[c]-relptrbase)
		else Dout(doveroc,-1);
end;